/***
 * FPU (CP1) Registers
 * Register constants and names required for the floating point unit.
 *
 * Control Registers
 * The FPU defines a set of control registers that offer configuration information
 * about the FPU which is accessible to the user.
 *
 * General Purpose Registers
 * The FPU defines a set of general purpose registers valid for 64 bit floats and ints
 **/

// Control Registers

// FIR - FP implmentation register
// FIR layout:
//  31        25 24   23   22   21  20  19   18   17  16 15           8 7        0
// |     0     | FC | 0 | F64 | L | W | 3D | PS | D | S | ProcessorID | Revision |
/*
	FC		- Full Convert Range, processor can convert all integers to floats without
			  causing an unimplmented instruction
	F64		- Indicates that there are 32 full double precision FP registers (rather than 16)
	L/W/D/S	- (L) indicates 64-bit integer support, (W) indicates 32-bit integer support,
			  (D) indicates 64-bit doubles are supported, and (S) indicates 32-bit floats are
			  supported.  Here support means that the instructions for these types exist.
	3D/PS	- (PS) indicates support for packed single SIMD instructions, (3D) adds further
			  support for MIPS-3D instructions.
	ProcID	- Generally the same processor ID as cp0, however, it will only return a non-zero
			  value if the FP has been implemented and is enabled via SR(CU1)
	Rev		- Implementation specific field.
*/

// FIR related constants
#define FPU_HAS_FLOAT				(0x1 << 16)
#define FPU_HAS_DOUBLE				(0x1 << 17)
#define FPU_HAS_SIMD				(0x1 << 18)
#define FPU_HAS_MIPS3D				(0x1 << 19)
#define FPU_HAS_WORD				(0x1 << 20)
#define FPU_HAS_DWORD				(0x1 << 21)
#define FPU_HAS_FULL_DOUBLE			(0x1 << 22)
#define FPU_HAS_FULL_CONVERT		(0x1 << 24)

// FIR access macros
#define FIR_Revision				0xFF
#define GET_FIR_Revision(f)			REG_GET_BITS(f, FIR_Revision, 0)
#define SET_FIR_Revision(f, v)		REG_SET_BITS(f, FIR_Revision, 0, v)

#define FIR_ProcID					0xFF00
#define GET_FIR_ProcID(f)			REG_GET_BITS(f, FIR_ProcID, 8)
#define SET_FIR_ProcID(f, v)		REG_SET_BITS(f, FIR_ProcID, 8, v)

// FCSR - FP Extensive control register
// FCSR Layout:
//  31     25 24    23    22   21 20 18 17 16    12 11      7 6     2 1  0
// | FCC7-1 | FS | FCC0 | FO | FN | 0 | E | Cause | Enables | Flags | RM |
/*
	FCC7-1
	FCC0		- Condition codes used for branch instructions are stored in these fields
	FS/FO/FN	- (FS) Flush to zero when result is too small to store in a FP register
				  (FO) Flush override detects denormalized operands and replaces them with zero
				  (FN) Flush to nearest, round denormalized numbers to the nearest normalized number
	E			- Indicates an unimplemented instruction exception was encountered
				  all instructions triggering this exception will leave the cause register and the
				  destination register untouched
	Cause
	Enables
	Flags		- Each of these fields is 5 bits wide and has one bit for each of the following IEEE
				  exceptions:
				  	(V) bit 4: invalid operating (e.g. sqrt(-1))
					(Z) bit 3: divide by 0
					(O) bit 2: overflow, result too large to represent
					(U) bit 1: underflow, result too small to represent
					(I) bit 0: inexact (rarely used)
				  The cause bits are set following an FP operation to indicate the result, (NOTE: cause
				  is undefined following an unimplemnted trap to a software emulator)
				  Tne enables bits specify which exceptions will generate CPU traps
				  The flags bits are a sticky version of cause and must be explicitly cleared to reset
	RM			- Rounding mode as required by IEEE 754
					0 RN: Round nearest
					1 RZ: Round toward zero
					2 RP: Round up
					3 RN: Round down
*/

// FCSR related constants
// For use in Cause/Enables/Flags fields:
#define FPU_EX_V	0x8
#define FPU_EX_Z	0x4
#define FPU_EX_U	0x2
#define FPU_EX_I	0x1
// RM field values:
#define FPU_ROUND_NEAR	0x0
#define FPU_ROUND_ZERO	0x1
#define FPU_ROUND_UP	0x2
#define FPU_ROUND_DOWN	0x3
// Flags:
#define FPU_FLUSH		(0x1 << 24)
#define FPU_FLUSH_NEAR	(0x1 << 21)
#define FPU_FLUSH_OVER	(0x1 << 22)
#define FPU_IMPL_EX		(0x1 << 17)

// FCSR access macros
#define FCSR_WRITE_MASK				0x163FFFF
#define FCSR_READ_MASK				0xFFE3FFFF

#define FCSR_RMode					0x3
#define GET_FCSR_RMode(f)			REG_GET_BITS(f, FCSR_RMode, 0)
#define SET_FCSR_RMode(f, v)		REG_SET_BITS(f, FCSR_RMode, 0, v)

#define FCSR_Flags					0x7C
#define GET_FCSR_Flags(f)			REG_GET_BITS(f, FCSR_Flags, 2)
#define SET_FCSR_Flags(f, v)		REG_SET_BITS(f, FCSR_Flags, 2, v)

#define FCSR_Enables				0xF80
#define GET_FCSR_Enables(f)			REG_GET_BITS(f, FCSR_Enables, 7)
#define SET_FCSR_Enables(f, v)		REG_SET_BITS(f, FCSR_Enables, 7, v)

#define FCSR_Cause					0x1F000
#define GET_FCSR_Cause(f)			REG_GET_BITS(f, FCSR_Cause, 12)
#define SET_FCSR_Cause(f, v)		REG_SET_BITS(f, FCSR_Cause, 12, v)

#define FCSR_FCC					0xFE800000
#define GET_FCSR_FCC(f)				((((f) >> 24) & 0xFE) | (((f) >> 23) & 0x1))
#define SET_FCSR_FCC(f, v)			(((f) & ~FCSR_FCC) | (((v) & 0xFE) << 24) | (((v) & 0x1) << 23))

// FCCR - FP condition codes
// FEXR - FP IEEE exception code information
// FENR - IEEE writable mask vector and flush/round options
// Thes registers all present different views of FCSR so we only provide
// macros for converting them to and from FCSR registers, notice that the
// fields are the same too, see above for descriptions
// NOTE: *_Pack(f) takes an FCSR formatted register and returns a (*) formatted register
//       *_Unpack(f) takes a (*) formatted register and returns an FCSR formatted register

// FCCR Layout:
//  31                8 7      0
// |        0         | FCC7-0 |

#define FCCR_MASK		FCSR_FCC
#define FCCR_Pack(f)	GET_FCSR_FCC(f)
#define FCCR_Unpack(f)	SET_FCSR_FCC(0, f)

// FEXR Layout:
//  31               18 17 16    12 11  7 6    2 1  0
// |        0         | E | Cause |  0  | Flags | 0 |

#define FEXR_MASK		0x3F07C
#define FEXR_Pack(f)	((f) & FEXR_MASK)
#define FEXR_Unpack(f)	((f) & FEXR_MASK)

// FENR Layout:
//  31               12 11     7 6    3  2  1   0
// |        0         | Enables |  0  | FS | RM |

#define FENR_MASK		0x10007C3
#define FENR_Pack(f)	(((f) & 0x7C3) | (((f) >> 22) & 0x4))
#define FENR_Unpack(f)	(((f) & 0x7C3) | (((f) & 0x4) << 22))

// Useful constants for FPU control registers
enum {
	kFPUCtl_FCSR		= 31,
	kFPUCtl_FIR			= 0,
	kFPUCtl_FCCR		= 25,
	kFPUCtl_FEXR		= 26,
	kFPUCtl_FENR		= 28,
};

// Useful constants for FPU GP registers
enum {
	kFPUReg_v0			= 0,	// FP return registers
	kFPUReg_v1			= 2,
	kFPUReg_a0			= 12,	// FP argument registers
	kFPUReg_a1			= 13,
	kFPUReg_a2			= 14,
	kFPUReg_a3			= 15,
	kFPUReg_a4			= 16,
	kFPUReg_a5			= 17,
	kFPUReg_a6			= 18,
	kFPUReg_a7			= 19,
#ifdef MIPS64					// MIPS64 specific conventions
	kFPUReg_s0			= 24,	// Subroutine FP registers (callee save)
	kFPUReg_s1			= 25,
	kFPUReg_s2			= 26,
	kFPUReg_s3			= 27,
	kFPUReg_s4			= 28,
	kFPUReg_s5			= 29,
	kFPUReg_s6			= 30,
	kFPUReg_s7			= 31,
	kFPUReg_t0			= 1,	// Temporary FP registers (caller save)
	kFPUReg_t1			= 3,
	kFPUReg_t2			= 4,
	kFPUReg_t3			= 5,
	kFPUReg_t4			= 6,
	kFPUReg_t5			= 7,
	kFPUReg_t6			= 8,
	kFPUReg_t7			= 9,
	kFPUReg_t8			= 10,
	kFPUReg_t9			= 11,
	kFPUReg_t10			= 20,
	kFPUReg_t11			= 21,
	kFPUReg_t12			= 22,
	kFPUReg_t13			= 23,
#else /* !MIPS64 */				// MIPS32 specific conventions
	kFPUReg_s0			= 20,	// Subroutine FP registers
	kFPUReg_s1			= 22,
	kFPUReg_s2			= 24,
	kFPUReg_s3			= 26,
	kFPUReg_s4			= 28,
	kFPUReg_s5			= 30,
	kFPUReg_t0			= 4,	// Temporary FP registers
	kFPUReg_t1			= 6,
	kFPUReg_t2			= 8,
	kFPUReg_t3			= 10,
	kFPUReg_t4			= 16,
	kFPUReg_t5			= 18,
	kFPUReg_t6			= 1,
	kFPUReg_t7			= 3,
	kFPUReg_t8			= 5,
	kFPUReg_t9			= 7,
	kFPUReg_t10			= 9,
	kFPUReg_t11			= 11,
	kFPUReg_t12			= 13,
	kFPUReg_t13			= 15,
	kFPUReg_t14			= 17,
	kFPUReg_t15			= 19,
	kFPUReg_t16			= 21,
	kFPUReg_t17			= 23,
	kFPUReg_t18			= 25,
	kFPUReg_t19			= 27,
	kFPUReg_t20			= 29,
	kFPUReg_t21			= 31,
#endif /* !MIPS64 */
};

